"""
A Handler policy is used to determine what should happen once a spell has been [activated]. Typically, either you want
to have immediate results happen (such as the Haste or Plant Forest Spell), or you want to target something to affect with the spell
(such as almost any of the other spells). Each of these cases has their own Handler policy you can use.
"""

import CvUtil
from CvPythonExtensions import *
from GerikSpells import GerikSpellBase
import GerikSpellsInterface

gc = CyGlobalContext()

from GSGraphicsPolicies import GSPlot as GSPlotGraphics
from GSGraphicsPolicies import GSPieMenu as GSPieMenuGraphics

from GSTargetInfoPolicies import GSNoTarget
from GSTargetInfoPolicies import GSSinglePlotTarget

import GerikSpellsUtils
import copy # To make copies of the prototype target policies

class GSBase:
    def __init__(self):
        """
        Arguments:
            @None

        Returns:
            >None

        Description:
            >None
        <FuncImplemented>
        """
        pass
    
    def start(self, bAlt, bShift, bCtrl):
        """
        Arguments:
            @None

        Returns:
            >None

        Description:
            >None
        <FuncNotImplemented>
        """
        raise NotImplementedError

    def stop(self, bCancel):
        """
        Arguments:
            @bCancel
        
        Returns:
            >None

        Description:
            >Stops the handler system. Typically called to remove any targetting graphics
        <FuncNotImplemented>
        """
        raise NotImplementedError

    def createTargettingStageList(self, bAlt, bShift, bCtrl):
        """
        Arguments:
            @bAlt: True or False if the alt key was held down when the spell was activated.
            @Shiftt: True or False if the alt key was held down when the spell was activated.
            @bCtrl: True or False if the alt key was held down when the spell was activated.

        Returns:
            >A list of (Graphics Policy, TargetInfo policy) tuples.
        
        Description:
            >Each tuple in the list represents one stage in the targetting.

        <FuncNotImplemented>
        """
        raise NotImplementedError

class GSImmediateNoTarget(GSBase):
    """
    This handler policy can be used to start the spells results directly after the spells activation.
    It uses a No Target TargetInfo policy to pass to any result policies.
    """
    def __init__(self):
        """
        Arguments:
            @None

        Returns:
            >None

        Description:
            >None
        <FuncImplemented>
        """
        GSBase.__init__(self)
    
    def start(self, bAlt, bShift, bCtrl):
        """
        Arguments:
            @None

        Returns:
            >None

        Description:
            >None
        <FuncImplemented>
        """
        self.castSpell(GSNoTarget())

    def stop(self, bCancel):
        """
        Arguments:
            @None
        Returns:
            >None
        Description:
            >This function is not used with this policy.
        <FuncNotNeeded>
        """
        return

    def createTargettingStageList(self, bAlt, bShift, bCtrl):
        """
        Arguments:
            @bAlt: True or False if the alt key was held down when the spell was activated.
            @Shiftt: True or False if the alt key was held down when the spell was activated.
            @bCtrl: True or False if the alt key was held down when the spell was activated.

        Returns:
            >A list of (Graphics Policy, TargetInfo policy) tuples.
        
        Description:
            >This function is not used in this policy.

        <FuncNotNeeded>
        """
        return

class GSImmediatePlotTarget(GSBase):
    """
    This works exactly like the GSImmediateNoTargert policy in that it starts the spell results
    directly after spell activation. The difference is that rather than passing a No Target
    TargetInfo policy, it will pass a Single Plot target info policy, where the plot
    is the plot that the casting group is standing on when it casts the spell. Useful for
    allowing immediate spells results combined with a policy like [GSResultsTargetCounter].
    """
    def __init__(self):
        """
        Arguments:
            @None

        Returns:
            >None

        Description:
            >None
        <FuncImplemented>
        """

    def start(self, bAlt, bShift, bCtrl):
        """
        Arguments:
            @None

        Returns:
            >None

        Description:
            >None
        <FuncImplemented>
        """
        pPlotTargetInfo = GSSinglePlotTarget()
        pPlotTargetInfo.setTarget(self.getCastingGroup().plot())
        self.castSpell(pPlotTargetInfo)

    def stop(self, bCancel):
        """
        Arguments:
            @None
        Returns:
            >None
        Description:
            >This function is not used with this policy.
        <FuncNotNeeded>
        """
        return

    def createTargettingStageList(self, bAlt, bShift, bCtrl):
        """
        Arguments:
            @bAlt: True or False if the alt key was held down when the spell was activated.
            @Shiftt: True or False if the alt key was held down when the spell was activated.
            @bCtrl: True or False if the alt key was held down when the spell was activated.

        Returns:
            >A list of (Graphics Policy, TargetInfo policy) tuples.
        
        Description:
            >This function is not used in this policy.

        <FuncNotNeeded>
        """
        return
    

#
# A base class for policies that use a graphical targetting system
#
class GSTargetting(GSBase):
    def __init__(self):
        """
        Arguments:
            @None

        Returns:
            >None

        Description:
            >None
        <FuncImplemented>
        """
        GSBase.__init__(self)

        self.m_pTargetPolicyList = []
        self.m_pGraphicsPolicyList = []
        self.m_pTargetInfoResults = []
        self.m_iNumberOfStages = 0
        self.m_iStage = 0
        self.m_pCurrentGraphicsPolicy = None


    def getGraphicsPolicy(self):
        """
        Arguments:
            @None

        Returns:
            >The current graphics policy.

        Description:
            >Used internally to get the current graphics policy being run.
        """
        return self.m_pCurrentGraphicsPolicy

    def setGraphicsPolicy(self, pNewPolicy):
        """
        Arguments:
            @pNewPolicy: The new graphics policy to be set as current.

        Returns:
            >None

        Description:
            >Used internally to set the current graphics policy being run.
        """
        self.m_pCurrentGraphicsPolicy = pNewPolicy

    def getTargettingResults(self, iStage):
        """
        Arguments:
            @iStage: The stage number you wish to recieve the TargetInfo policy from, starting with stage 0.

        Returns:
            >A TargetInfo class of the target selected during that stage.
            >None if not target was yet selected for that stage.

        Description:
            >Typically, when about to cast a spell, the castSpell function in the [GSHandlerPolicies] only get the last
            target picked. Use this to get TargetInfos picked during a stage other than the last.
        """
        return self.m_pTargetInfoResults[iStage]

    def start(self, bAlt, bShift, bCtrl):
        """
        Arguments:
            @bAlt: True if the Alt button was being held down when the spell was [activated].
            @bShift: True if the Shift button was being held down when the spell was [activated].
            @bCtrl: True if the Ctrl button was being held down when the spell was [activated].

        Returns:
            >None

        Description:
            >None
        <FuncImplemented>
        """
        pPolicyPairList = self.createTargettingStageList(bAlt, bShift, bCtrl)
        self.m_pTargetPolicyList = []
        self.m_pGraphicsPolicyList = []
        self.m_pTargetInfoResults = []
        self.m_iNumberOfStages = 0
        self.m_iStage = 0
        self.m_pCurrentGraphicsPolicy = None

        for tPair in pPolicyPairList:
            self.m_pTargetPolicyList.append(tPair[0])
            self.m_pGraphicsPolicyList.append(tPair[1])
            self.m_pTargetInfoResults.append(None)
            self.m_iNumberOfStages += 1

        self.setGraphicsPolicy(self.m_pGraphicsPolicyList[0])
        GerikSpellsInterface.setLocalInterfaceModeToSpell()

        # Builds the range list for this stage.
        pRangePlotList = []
        if (self.m_pTargetPolicyList[0].getRangeMakerCallback()):
            pRangePlotList = self.m_pTargetPolicyList[0].getRangeMakerCallback()(self.getCastingPlot())
        self.getGraphicsPolicy().start(self.m_pTargetPolicyList[0], self.handleInput, pRangePlotList)


    def stop(self, bCancel):
        """
        Arguments:
            @bCancel: If the targetting should stop because the user cancelled the spell.

        Returns:
            >None

        Description:
            >Tells the targetting system to stop. This will undraw all colored plots and close any targetting screens.
            Since a slightly different process must be done to cancel a targetting system than simply close it when it
            has no more targets to find, the bCancel argument is required.
        <FuncImplemented>
        """
        gp = self.getGraphicsPolicy()
        if (gp):
            gp.stop(bCancel)
        self.setGraphicsPolicy(None)

        # The interface mode must be reset by the screens if the user cancelled, because screens can wait until after
        # the escape key is handled by the system before turning off the interface mode. This allows for the escape key
        # not to be handled with the interface mode back to normal, which would result in the main menu screen opening
        # whenever a spell is cancelled with "escape".
        if not bCancel:
            GerikSpellsInterface.resetLocalInterfaceMode()

    def handleInput(self, pSelectedTargetInfo):
        """
        Arguments:
            @pSelectedTargetInfo: The TargetInfo policy class of the target selected.

        Returns:
            >None

        Description:
            >This function will take in the input of one of the stages of the targetting, save it, then start
            the next stage. If the stage was the last stage, it saves the results as well as calling castSpell to say
            that targetted is complete and the spell can now move onto the results phase.

            >The result (a TargetInfo class) of each targetting stage is stored into the self.m_pTargetInfoResults variable, which can be
            easily accessed by using the getTargettingResults(i) function, where i is the stage whose resulting TargetInfo you
            wish to recieve.
        """
        if (pSelectedTargetInfo.isCurrentTargetValid(self.getCastingGroup(), self.getCastingPlot())):

            # Save the target.
            self.m_pTargetInfoResults[self.m_iStage] = pSelectedTargetInfo

            # Close the stage, and progress
            self.getGraphicsPolicy().stop(False)
            self.m_iStage += 1

            # If we still have more stages to go:
            if (self.m_iStage < self.m_iNumberOfStages):
                pCastGroup = self.getCastingGroup()
                pCastPlot = self.getCastingPlot()

                if (self.m_pTargetPolicyList[self.m_iStage].isPlotTargetPolicy()):
                    # Plots don't have pre-defined targets built in, so the target info doesn't need to be created.
                    self.setGraphicsPolicy(self.m_pGraphicsPolicyList[self.m_iStage])
                    self.getGraphicsPolicy().start(self.m_pTargetPolicyList[self.m_iStage], self.handleInput, None)
                else:
                    # Get the list of targettable items.
                    pTotalTargetTuple = pSelectedTargetInfo.getSecondaryTargetList(pCastGroup, pCastPlot)
                    pTargetList = self.m_pTargetPolicyList[self.m_iStage].getTargettableList(pCastGroup, pCastPlot, pTotalTargetTuple[0])
                    pTargetList.extend(self.m_pTargetPolicyList[self.m_iStage].getTargettableList(pCastGroup, pCastPlot, pTotalTargetTuple[1]))

                    #Start the graphics policy.
                    self.setGraphicsPolicy(self.m_pGraphicsPolicyList[self.m_iStage])
                    self.getGraphicsPolicy().start(self.m_pTargetPolicyList[self.m_iStage], self.handleInput, pTargetList)

                    
            else:
                self.stop(False)
                self.castSpell(pSelectedTargetInfo)
        else:
            CvUtil.pyPrint("Warning: Cancelled handling input due to selected target not being targettable")
            self.stop(True)

